package com.ruubypay.miss.hystrix;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.netflix.hystrix.*;
import com.ruubypay.miss.config.ConfigKey;
import com.ruubypay.miss.config.DefaultHystrixConfig;
import com.ruubypay.miss.returntype.ModelsReturn;

import java.util.Map;

/**
 * dubbo熔断器
 * @author chenhaiyang
 */
public class DubboHystrixCommand extends HystrixCommand<Result> {

    private Invoker<?> invoker;
    private Invocation invocation;
    
    public DubboHystrixCommand(Invoker<?> invoker, Invocation invocation){
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(invoker.getInterface().getName()))
                    .andCommandKey(HystrixCommandKey.Factory.asKey(String.format("%s_%d", invocation.getMethodName(),
                                                                                 invocation.getArguments() == null ? 0 : invocation.getArguments().length)))
              .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                                            .withMetricsRollingStatisticalWindowInMilliseconds(getConfig(invoker.getUrl(),ConfigKey.METRICS_ROLLING_STATISTICALWINDOWIN_MILLISECONDS.getKeyName(),DefaultHystrixConfig.DEFAULT_METRICS_ROLLING_STATISTICALWINDOWIN_MILLISECONDS))
                                            .withCircuitBreakerRequestVolumeThreshold(getConfig(invoker.getUrl(),ConfigKey.CIRCUIT_BREAKER_REQUEST_VOLUMETHRESHOLD.getKeyName(),DefaultHystrixConfig.DEFAULT_CIRCUIT_BREAKER_REQUEST_VOLUMETHRESHOLD))
                                            .withCircuitBreakerSleepWindowInMilliseconds(getConfig(invoker.getUrl(),ConfigKey.CIRCUIT_BREAKER_SLEEPWINDOWINMILLISECONDS.getKeyName(),DefaultHystrixConfig.DEFAULT_CIRCUIT_BREAKER_SLEEPWINDOWINMILLISECONDS))
                                            .withCircuitBreakerEnabled(getConfig(invoker.getUrl(),ConfigKey.CIRCUIT_BREAKERENABLED.getKeyName(),DefaultHystrixConfig.DEFAULT_CIRCUIT_BREAKERENABLED))
                                            .withCircuitBreakerErrorThresholdPercentage(getConfig(invoker.getUrl(),ConfigKey.CIRCUIT_BREAKERERROR_THRESHOLDPERCENTAGE.getKeyName(),DefaultHystrixConfig.DEFAULT_CIRCUIT_BREAKERERROR_THRESHOLDPERCENTAGE))
                                            .withCircuitBreakerForceOpen(getConfig(invoker.getUrl(),ConfigKey.CIRCUIT_BREAKERFORCE_OPEN.getKeyName(),DefaultHystrixConfig.DEFAULT_CIRCUIT_BREAKERFORCE_OPEN))
                                            .withCircuitBreakerForceClosed(getConfig(invoker.getUrl(),ConfigKey.CIRCUIT_BREAKERFORCE_CLOSED.getKeyName(),DefaultHystrixConfig.DEFAULT_CIRCUIT_BREAKERFORCE_CLOSED))
                                            .withExecutionTimeoutInMilliseconds(getConfig(invoker.getUrl(),ConfigKey.EXECUTI_ONTIMEOUT_INMILLISECONDS.getKeyName(),DefaultHystrixConfig.DEFAULT_EXECUTI_ONTIMEOUT_INMILLISECONDS))
                                            .withExecutionTimeoutEnabled(getConfig(invoker.getUrl(),ConfigKey.EXECUTI_ONTIMEOUT_ENABLED.getKeyName(),DefaultHystrixConfig.DEFAULT_EXECUTI_ONTIMEOUT_ENABLED)))
              .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(getConfig(invoker.getUrl(),ConfigKey.THREAD_POOL_CORDE_SIZE.getKeyName(),DefaultHystrixConfig.DEFAULT_THREADPOOL_CORE_SIZE))));
        this.invoker=invoker;
        this.invocation=invocation;
    }

    /**
     * int类型的属性，获取配置值
     * @param url dubbo url
     * @param key 配置键
     * @param defaultValue 默认值
     * @return 返回结果
     */
    private static int getConfig(URL url,String key,int defaultValue){
        int value = defaultValue;
        if (url != null) {
            value = url.getParameter(key,defaultValue);
        }
        return value;
    }

    /**
     * boolean 类型的属性，获取配置值
     * @param url dubbo url
     * @param key 配置键
     * @param defaultValue 默认值
     * @return 返回结果
     */
    private static boolean getConfig(URL url,String key,boolean defaultValue){
        boolean  value = defaultValue;
        if (url != null) {
            value = url.getParameter(key,defaultValue);
        }
        return value;
    }

    @Override
    protected Result run() throws Exception {
        return invoker.invoke(invocation);
    }

    /**
     * 错误回调
     * @return 返回当熔断器执行后程序结果
     */
    @Override
    protected Result getFallback() {
       return new Result() {
           @Override
           public Object getValue() {
              return null;
           }

           @Override
           public Throwable getException() {
               return null;
           }

           @Override
           public boolean hasException() {
               return false;
           }

           @Override
           public Object recreate() throws Throwable {
               ModelsReturn hystrixReturn =  new ModelsReturn<String>();
               hystrixReturn.setResCode(invoker.getUrl().getParameter(ConfigKey.ERROR_CODE.getKeyName()));
               hystrixReturn.setResMessage("被调用方服务状态异常，熔断器已熔断该请求");
               return hystrixReturn;
           }

           @Override
           public Object getResult() {
               return null;
           }

           @Override
           public Map<String, String> getAttachments() {
               return null;
           }

           @Override
           public String getAttachment(String s) {
               return null;
           }

           @Override
           public String getAttachment(String s, String s1) {
               return null;
           }
       };
    }
}
